﻿// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <osgViewer/viewer>
#include <osgEarth/Notify>
#include <osgEarth/EarthManipulator>
#include <osgEarth/MapNode>
#include <osgEarth/Threading>
#include <osgEarth/ShaderGenerator>
#include <osgDB/ReadFile>
#include <osgGA/TrackballManipulator>
#include <osgUtil/Optimizer>
#include <iostream>
#include <osgEarth/Metrics>
#include <osgEarth/GDAL>

#include <osgEarth/OGRFeatureSource>
#include <osgEarth/FeatureDisplayLayout>
#include <osgEarth/FeatureModelLayer>
#include <osgEarth/TMS>
#include <osgEarth/XYZ>


#include <osgEarth/TerrainEngineNode>

#include <osgEarth/TFS> //tiled feature services

#include <osgEarth/SimplePager>
#include "SimpleOsmBuildingsLayer.h"

using namespace osgEarth;
using namespace osgEarth::Util;
#include <iostream>
#include <sstream>
#include <cassert>
using namespace std;
//SKY
#include "osgEarthDrivers\sky_simple\SimpleSkyNode"//for the sky
#include "osgEarthDrivers\sky_simple\SimpleSkyOptions"//for the sky

//Mouse coordinate Tools
#include <osgEarth/MouseCoordsTool>
string g_myMouseCallbackText;
bool g_debugMouseAltitude = false;


SkyNode* g_skyNode = nullptr;
SimplePager* g_simplePager = nullptr;
osgEarth::MapNode* g_mapNode = nullptr;


struct MyMouseCallback :public osgEarth::Contrib::MouseCoordsTool::Callback
{
	virtual void 	set(const GeoPoint& coords, osg::View* view, MapNode* mapNode);
	virtual void 	reset(osg::View* view, MapNode* mapNode);
};
void MyMouseCallback::set(const GeoPoint& coords, osg::View* view, MapNode* mapNode) {
	if (g_debugMouseAltitude == true) {
		//cout << "debug mouse alt" << endl; 
		osgEarth::GeoPoint gp(osgEarth::SpatialReference::get("wgs84"), 116.1639, 40.2501 ,0 , osgEarth::AltitudeMode::ALTMODE_ABSOLUTE);
		osgEarth::GeoPoint gp2(osgEarth::SpatialReference::get("wgs84"), 116.1639, 40.2501, 0, osgEarth::AltitudeMode::ALTMODE_RELATIVE);
		bool ok1 = gp.makeAbsolute(g_mapNode->getTerrain());
		bool ok2 = gp2.makeAbsolute(g_mapNode->getTerrain());
		//cout << "debug ok1 ok2 " << ok1 << " " << ok2 << endl;
		g_debugMouseAltitude = false;
	}
	stringstream ss;
	ss << "Lon:" << coords.x() << ",Lat:" << coords.y() << ",Alt:" << coords.alt();
	g_myMouseCallbackText = ss.str();
}
void MyMouseCallback::reset(osg::View* view, MapNode* mapNode) {
	g_myMouseCallbackText = "Lon:---,Lat:---,Alt:---";//lon , lat , alt
}


struct MyMapNodeCallback :osg::NodeCallback
{
	MyMapNodeCallback(osgViewer::Viewer* viewer):_viewer(viewer){ }
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
	osg::observer_ptr<SimpleOSMBuildingsLayer> _osmLayer;
protected:
	bool _mcToolInited = false;
	osg::observer_ptr<osgViewer::Viewer> _viewer;
	bool _osmInstalled = false;
	
};
void MyMapNodeCallback::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
	osgEarth::MapNode* mapNode = dynamic_cast<osgEarth::MapNode*>(node);
	if (mapNode) {
		if (_osmLayer.valid() && _osmInstalled==false) {
			_osmInstalled = true;
			
			mapNode->getLayerNodeGroup()->addChild(_osmLayer.get());
			_osmLayer->build();
		}
		if (mapNode->getTerrainEngine()) {
			//Mouse Coords
			if (_mcToolInited == false) {
				_mcToolInited = true;
				osg::ref_ptr<osgEarth::Contrib::MouseCoordsTool> mouseCoordsTool = new osgEarth::Contrib::MouseCoordsTool(mapNode);
				mouseCoordsTool->addCallback(new MyMouseCallback);
				_viewer->addEventHandler(mouseCoordsTool);
			}
		}
	}
	traverse(node, nv);
}






class FindSkyNodeVisitor :public osg::NodeVisitor {
public:
	FindSkyNodeVisitor() :osg::NodeVisitor(NODE_VISITOR, TRAVERSE_ALL_CHILDREN) {}
	virtual void apply(osg::Group& node) {
		SkyNode* sky = dynamic_cast<SkyNode*>(&node);
		if (sky != nullptr) {
			g_skyNode = sky;
		}
		else {
			traverse(node);
		}
	}
};
class FindSkyCallback :public osg::NodeCallback {
public:
	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {
		if (g_skyNode == nullptr) {
			FindSkyNodeVisitor findSkyVisitor;
			node->accept(findSkyVisitor);
			traverse(node, nv);
		}
		else {
			return;
		}
	}
};

//IMGUI
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "OsgImGuiHandler.hpp"
//初始化IMGUI
class ImGuiInitOperation : public osg::Operation
{
public:
	ImGuiInitOperation()
		: osg::Operation("ImGuiInitOperation", false){}
	void operator()(osg::Object* object) override
	{
		osg::GraphicsContext* context = dynamic_cast<osg::GraphicsContext*>(object);
		if (!context)return;
		if (!ImGui_ImplOpenGL3_Init()){std::cout << "ImGui_ImplOpenGL3_Init() failed\n";}
	}
};


void getAllChildNodeCount(osg::Group* group, int & count) {
	for (int ic = 0; ic < group->getNumChildren(); ++ic) {
		if (dynamic_cast<osg::Geode*>(group->getChild(ic))) {
			++count;
		}
		else {
			osg::Group* group1 = dynamic_cast<osg::Group*>(group->getChild(ic));
			if (group1) {
				getAllChildNodeCount(group1, count);
			}
		}
		
	}
}


//IMGUI控件绘制类
class MyIMGuiHandler : public OsgImGuiHandler
{
public:
	MyIMGuiHandler() {}
protected:
	void drawUi() override //绘制控件
	{
		// ImGui code goes here...
		ImGui::Begin("tut-30");
		ImGui::Text("hours");
		float tempHours = _hours;
		ImGui::SliderFloat("slider", &tempHours, 0.0f, 4.f);
		if (tempHours != _hours) {
			_hours = tempHours;
			if (g_skyNode != nullptr) {
				float dayhour = 24 * (_hours - floorf(_hours));
				const osgEarth::DateTime& dt = g_skyNode->getDateTime();
				osgEarth::DateTime dt1(dt.year(), dt.month(), dt.day(), dayhour);
				g_skyNode->setDateTime(dt1);
			}
		}
		if (g_simplePager != nullptr) {
			stringstream ss;
			int num = 0;
			//getAllChildNodeCount(g_simplePager, num);
			
			ss << "num child of SP:" << num;
			ImGui::Text(ss.str().c_str());
		}
		else {
			ImGui::Text("num child of SP:0");
		}
		ImGui::Checkbox("debug mouse alt", &g_debugMouseAltitude);
		ImGui::Text(g_myMouseCallbackText.c_str());
		ImGui::End();
	}
private:
	float _hours = 0;
};


 


int main()
{
	cout << "version 1.0.0.0 2024/2/11" << endl; 
	cout << "version 1.1.0.1 feature: building bottom on terrain. 2024/2/12" << endl;
	cout << "version 1.1.0.2 feature: building bottom on terrain. 2024/2/13" << endl;
	cout << "version 1.1.0.3 fix: layer trigger update buildings heights 2024/2/15" << endl;
	cout << "version 1.2.0.0 feature: renumber to 30; try use pbf vector tile data. 2024/2/17" << endl; 
	//pbf内部数据格式和json的还是不一样，14级似乎缺了不少内容，这里时间关系不再继续pbf研究了，
	//采用osmbuildings网站爬取的数据写tut30吧。马上上班了，没时间仔细研究pbf了。2024/2/17
	cout << "version 1.3.0.0 fix: return to json format for tut-30 docs. 2024/2/17 21:57" << endl; 


	osgEarth::initialize();
	//create a viewer
	osgViewer::Viewer viewer;
	viewer.setUpViewInWindow(0, 100, 1280, 720);
	viewer.setReleaseContextAtEndOfFrameHint(false);
	//set camera manipulator
	EarthManipulator* manipulate = new EarthManipulator;
	viewer.setCameraManipulator(manipulate);
	osg::ref_ptr<osgEarth::Map> map = new osgEarth::Map();

	////layer
	//GDALImageLayer* layer1 = new GDALImageLayer();
	//layer1->setURL(osgEarth::URI("E:/coding/osgearth2023/osgearth3.4/data/world.tif"));
	//map->addLayer(layer1);

	//bg osm
	XYZImageLayer* bgLayer = new XYZImageLayer();
	bgLayer->setURL(osgEarth::URI("https://api.maptiler.com/maps/basic-v2/256/{z}/{x}/{y}.png?key=4SbPVVkPORGgXetw2vsf"));
	//https://tile.openstreetmap.org/7/112/44.png
	// https://tile-a.openstreetmap.fr/hot/15/26978/12382.png
	bgLayer->setURL(osgEarth::URI("https://tile-a.openstreetmap.fr/hot/{z}/{x}/{y}.png"));
	bgLayer->setProfile(Profile::create(Profile::SPHERICAL_MERCATOR));//need this for XYZ
	map->addLayer(bgLayer);

	////elevation
	TMSElevationLayer* elev = new TMSElevationLayer();
	elev->setURL(osgEarth::URI("http://readymap.org/readymap/tiles/1.0.0/116/"));
	elev->setVerticalDatum("egm96");
	elev->setMinLevel(0);
	elev->setMaxLevel(15);
	map->addLayer(elev);

	////local elevation
	//GDALElevationLayer* elev2 = new GDALElevationLayer;
	//elev2->setURL(osgEarth::URI("E:/coding/osgearth2023/osgearth3.4/data/dem_cog_0.0833.tif"));
	//map->addLayer(elev2);

	//MapNode
	osg::ref_ptr<osgEarth::MapNode> mapNode = new osgEarth::MapNode(map.get());
	viewer.setSceneData(mapNode);
	MyMapNodeCallback* myMapNodeCallback = new MyMapNodeCallback(&viewer);
	mapNode->addUpdateCallback(myMapNodeCallback);
	g_mapNode = mapNode.get();



	if (false)//这个实验成功了，很棒的功能，帮忙实现了基于Viewer的TileKey生成，瓦片数据请求，Cull裁剪，自动释放功能。
	{//tiled feature services
		SimplePager* simplePager = new SimplePager(map.get(), osgEarth::Profile::create("spherical-mercator"));
		simplePager->setMinLevel(15);
		simplePager->setMaxLevel(15);
		simplePager->setRangeFactor(3);
		simplePager->build();
		//mapNode->addChild(simplePager);
		//g_simplePager = simplePager;
	}
	if (true)//this is my osmbuildings staffs
	{
		SimpleOSMBuildingsLayer* myOSMBuildingsLayer = new SimpleOSMBuildingsLayer(map,mapNode.get());
		myOSMBuildingsLayer->setUrl("http://localhost:8080/beijing_osmbuildings_tiles/{z}/{x}/{y}.json");
		
		//this is for new PBF vector tile services http://localhost:8080/mbtiles/?db=beijing.mbtiles&z=8&x=209&y=158
		//myOSMBuildingsLayer->setUrl("http://localhost:8080/mbtiles/?db=beijing.mbtiles&z={z}&x={x}&y={y}");
		
		//myOSMBuildingsLayer->build();
		//mapNode->addChild(myOSMBuildingsLayer);
		g_simplePager = myOSMBuildingsLayer;
		myMapNodeCallback->_osmLayer = myOSMBuildingsLayer;
	}

	if(false)//这个已经实验成功了，GeoTransfrom在MapNode下面正常。
	{//测试一个普通几何体是否会出现随着DEM变化而变化的情况

		osgEarth::GeoTransform* geoTrans = new osgEarth::GeoTransform;
		osg::ShapeDrawable* draw = new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), 1000, 1000, 2));
		draw->setColor(osg::Vec4(1, 0, 0, 0.5));
		osg::Geode* geode = new osg::Geode;
		geode->addDrawable(draw);
		geoTrans->addChild(geode);
		geoTrans->setPosition(osgEarth::GeoPoint(osgEarth::SpatialReference::get("wgs84"), 116.16394043, 40.25018405));
		//验证以下geotransform不直接放在mapNode下面是否能正确自动匹配地形，试过了结论是可以的。
		osg::Group* tempgroup1 = new osg::Group;
		osg::Group* tempgroup2 = new osg::Group;
		tempgroup1->addChild(geoTrans);
		tempgroup2->addChild(tempgroup1);
		mapNode->addChild(tempgroup2);
	}
	


	

	//one building testing codes. 一个建筑，本地json建筑的绘制代码用于测试
	//if(true)
	//{
	//	//1 reading building datas
	//	vector<OSMBuildingFeature> features;
	//	OSMBuildingFeature::ReadBuildingFeaturesFromVectorFile(
	//		"E:/Programs/XAMPP/htdocs/beijing_osmbuildings_tiles/15/26930/12365.json", features);
	//	//ReadBuildingFeatures("E:/coding/osgearth2023/osgearth3.4/data/osmbuildings-test-roof.json", features);
	//	//2 计算geojson全部建筑的中心点
	//	osg::Vec2d jsonCenter = OSMBuildingFeature::computeAllBuildingsCenter(features);
	//	double centerHeight = 0;
	//	if (mapNode->getTerrain()) {
	//		double msl;
	//		mapNode->getTerrain()->getHeight(osgEarth::SpatialReference::get("wgs84"), jsonCenter.x(), jsonCenter.y(), &msl, &centerHeight);
	//	}
	//	//3 make building geometies
	//	osg::ref_ptr<osg::Vec3dArray> vertexArray = new osg::Vec3dArray;
	//	osg::ref_ptr<osg::Vec3Array> normalArray = new osg::Vec3Array;
	//	osg::ref_ptr<osg::Vec3Array> colorArray = new osg::Vec3Array;
	//	for (int ifea = 0; ifea < features.size(); ++ifea)
	//	{
	//		OSMBuildingFeature::buildOsmBuildingGeometryWithCenterPoint(features[ifea], jsonCenter,
	//			centerHeight,
	//			mapNode->getTerrain(),
	//			vertexArray.get(),normalArray.get(),colorArray.get());
	//		//cout << ifea << "/" << features.size() <<" nverts "<< vertexArray->size() << endl;
	//	}
	//	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
	//	geom->setVertexArray(vertexArray.get());
	//	geom->setNormalArray(normalArray.get(), osg::Array::Binding::BIND_PER_VERTEX);
	//	geom->setColorArray(colorArray.get(), osg::Array::Binding::BIND_PER_VERTEX);
	//	geom->getOrCreateStateSet()->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
	//	geom->getOrCreateStateSet()->setAttribute(new osg::CullFace(osg::CullFace::BACK));
	//	geom->getOrCreateStateSet()->setAttribute(
	//		new osg::FrontFace(osg::FrontFace::COUNTER_CLOCKWISE));
	//	osg::ref_ptr<osg::DrawArrays> drawA = new osg::DrawArrays(GL_TRIANGLES, 0, vertexArray->size());
	//	geom->addPrimitiveSet(drawA.get());
	//	osg::ref_ptr<osg::Geode> geode = new osg::Geode();
	//	geode->addDrawable(geom.get());
	//	g_debugBuildingGeode = geode;
	//	cout << "numTri " << vertexArray->size() / 3 << endl;
	//	cout << "numVert " << vertexArray->size() << endl;
	//	osg::ref_ptr<osgEarth::GeoTransform> transform = new osgEarth::GeoTransform();

	//	transform->setPosition(osgEarth::GeoPoint(osgEarth::SpatialReference::get("wgs84"), jsonCenter.x(), jsonCenter.y()));
	//	transform->addChild(geode.get());
	//	mapNode->addChild(transform.get());
	//}
	
	
	if(true){//SKY
		//SkyOptions options;
		osgEarth::SimpleSky::SimpleSkyOptions options;
		options.quality() = SkyOptions::QUALITY_DEFAULT;
		options.ambient() = 0.01;//控制环境光强度
		options.exposure() = 10.f;//控制白天的亮度
		options.atmosphereVisible() = true;//大气层厚度是否可见
		options.atmosphericLighting() = true;//大气散射光是否可见
		options.sunVisible() = true;
		options.moonVisible() = true;
		options.starsVisible() = true;
		options.usePBR() = true;
		//添加天空模型
		mapNode->addExtension(Extension::create("sky_simple", options));//这里不能使用addChild, Extension是一种更复杂的对象
	}
	//"moutains", 115.866861, 40.330086, 800, 0, 0, 1000);
	//"Beijing", 116.39, 39.9, 800, 0, 0, 1000
	Viewpoint viewPoint1("Beijing", 116.143,40.2472, 139 , 45, -45, 2000);
	manipulate->setViewpoint(viewPoint1, 2);

	//IMGUI
	viewer.setRealizeOperation(new ImGuiInitOperation);//imgui
	viewer.addEventHandler(new MyIMGuiHandler);//imgui
	viewer.getCamera()->addUpdateCallback(new FindSkyCallback);

	return Metrics::run(viewer);
}